package pool

import (
	"sync"
	"util/log"
)

type ObjPool struct {
	pool     []interface{}
	count    int
	capacity int
}

func (p *ObjPool) Init(capacity int) {

	p.pool = make([]interface{}, capacity, capacity)
	p.count = 0
	p.capacity = capacity
}

func (p *ObjPool) Capacity() int {
	return p.capacity
}

func (p *ObjPool) Count() int {
	return p.count
}

func (p *ObjPool) IsFull() bool {
	if p.count >= len(p.pool) {
		return true
	}

	return false
}

func (p *ObjPool) GetEmptySlot() int {
	for i, v := range p.pool {
		if v == nil {
			return i
		}
	}

	return -1
}

func (p *ObjPool) SetSlot(index int, v interface{}) {
	if v == nil {
		panic("object not allow nil")
	}

	if p.pool[index] != nil {
		panic("obj pool's slot is not empty")
	}
	p.pool[index] = v
	p.count++
}

func (p *ObjPool) GetSlot(index int) interface{} {
	return p.pool[index]
}

func (p *ObjPool) ClearSlot(index int) {
	p.pool[index] = nil
	p.count--
}

func (p *ObjPool) expand(capacity int) {
	newpool := make([]interface{}, capacity, capacity)
	for i, o := range p.pool {
		newpool[i] = o
	}

	p.pool = newpool
	p.capacity = capacity
}

func (p *ObjPool) shrink(capacity int) {
	newpool := make([]interface{}, capacity, capacity)
	idx := 0
	for _, o := range p.pool {
		if o != nil {
			newpool[idx] = o
			idx++
		}
	}

	p.pool = newpool
	p.capacity = capacity
}

type PoolList struct {
	pools    []*ObjPool
	lock     sync.Mutex
	capacity int
	poolcap  int
}

//capacity总容量，如果为-1则没有容量限制
func (pl *PoolList) Init(capacity int, poolcap int) {
	if capacity == -1 {
		pl.pools = make([]*ObjPool, 0, 16)
		pl.poolcap = poolcap
	} else {
		pl.pools = make([]*ObjPool, 0, 1)
		newpool := &ObjPool{}
		newpool.Init(capacity)
		pl.pools = append(pl.pools, newpool)
		pl.poolcap = capacity
	}

	pl.capacity = capacity
}

func (pl *PoolList) PoolCap() int {
	return pl.poolcap
}

func (pl *PoolList) Expand(capacity int) {
	if pl.capacity == -1 {
		return
	}

	if len(pl.pools) < 1 || pl.capacity > capacity {
		return
	}

	pl.pools[0].expand(capacity)
	pl.poolcap = capacity
}

func (pl *PoolList) Shrink(capacity int) {
	if pl.capacity == -1 {
		return
	}

	if len(pl.pools) < 1 || pl.pools[0].count > capacity {
		return
	}

	pl.pools[0].shrink(capacity)
	pl.poolcap = capacity

}

func (pl *PoolList) Lock() {
	pl.lock.Lock()
}

func (pl *PoolList) Unlock() {
	pl.lock.Unlock()
}

func (pl *PoolList) Count() int {
	sum := 0
	for _, p := range pl.pools {
		sum += p.count
	}

	return sum

}

func (pl *PoolList) PoolLen() int {
	return len(pl.pools)
}

func (pl *PoolList) GetPool(si int) *ObjPool {
	if si < 0 || si >= len(pl.pools) {
		return nil
	}

	return pl.pools[si]
}

func (pl *PoolList) GetPoolSlot(index int) (int, int) {
	slot := -1
	poolslot := -1

	if pl.capacity == -1 {
		slot = int(index / pl.poolcap)
		poolslot = int(index % pl.poolcap)
	} else {
		slot = 0
		poolslot = index
	}

	return slot, poolslot
}

func (pl *PoolList) AddObject(index int, o interface{}) int {

	if pl.capacity != -1 && pl.Count() >= pl.capacity {
		log.LogError("(AddObject)pool's capacity is full")
		return -1
	}

	slot := -1
	poolslot := -1
	if index == -1 {
		for i, p := range pl.pools {
			if p.IsFull() {
				continue
			}
			slot = i
			poolslot = p.GetEmptySlot()
		}

		if pl.capacity == -1 && (slot == -1 || poolslot == -1) {
			newpool := &ObjPool{}
			newpool.Init(pl.poolcap)
			pl.pools = append(pl.pools, newpool)
			slot = len(pl.pools) - 1
			poolslot = 0
		}

	} else {
		if pl.capacity == -1 {
			slot = int(index / pl.poolcap)
			poolslot = int(index % pl.poolcap)
		} else {
			slot = 0
			poolslot = index
		}
	}

	if slot < 0 || slot >= len(pl.pools) {
		log.LogError("(AddObject)not found slot position")
		return -1
	}

	p := pl.pools[slot]
	if poolslot < 0 || poolslot >= p.capacity {
		log.LogError("(AddObject)not found slot position")
		return -1
	}

	if p.GetSlot(poolslot) != nil {
		log.LogError("(AddObject)pool slot is not empty")
		return -1
	}

	p.SetSlot(poolslot, o)

	return slot*p.capacity + poolslot
}

func (pl *PoolList) FindIndex(obj interface{}) int {
	for si, ps := range pl.pools {
		for pi, p := range ps.pool {
			if p != nil && p == obj {
				return si*ps.capacity + pi
			}
		}
	}
	return -1
}

func (pl *PoolList) GetFirstChild() (interface{}, int) {
	for si, ps := range pl.pools {
		for pi, p := range ps.pool {
			if p != nil {
				return p, si*ps.capacity + pi
			}
		}
	}

	return nil, -1
}

func (pl *PoolList) GetNextChild(idx int) (interface{}, int) {
	capacity := pl.capacity
	if pl.capacity == -1 {
		capacity = len(pl.pools) * pl.poolcap
	}
	idx++
	for ; idx < capacity; idx++ {

		si := -1
		pi := -1

		if pl.capacity == -1 {
			si = idx / pl.poolcap
			pi = idx % pl.poolcap
		} else {
			si = 0
			pi = idx
		}

		if p := pl.pools[si].GetSlot(pi); p != nil {
			return p, idx
		}

	}

	return nil, -1
}

func (pl *PoolList) getIndex(obj interface{}) (int, int) {
	for si, ps := range pl.pools {
		for pi, p := range ps.pool {
			if p != nil && p == obj {
				return si, pi
			}
		}
	}

	return -1, -1
}

func (pl *PoolList) DelObjectByPtr(obj interface{}) bool {
	si, pi := pl.getIndex(obj)
	if si != -1 && pi != -1 {
		pl.pools[si].ClearSlot(pi)
		return true
	}
	return false
}

func (pl *PoolList) DelObject(index int) bool {
	slot := -1
	poolslot := -1

	if pl.capacity == -1 {
		slot = int(index / pl.poolcap)
		poolslot = int(index % pl.poolcap)
	} else {
		slot = 0
		poolslot = index
	}

	if slot < 0 || slot >= len(pl.pools) {
		log.LogError("(DelObject)not found slot position,", slot)
		return false
	}

	p := pl.pools[slot]
	if poolslot < 0 || poolslot >= p.capacity {
		log.LogError("(DelObject)not found pool slot position,", poolslot)
		return false
	}

	p.ClearSlot(poolslot)

	return true
}

func (pl *PoolList) GetObject(index int) interface{} {

	slot := -1
	poolslot := -1

	if pl.capacity == -1 {
		slot = int(index / pl.poolcap)
		poolslot = int(index % pl.poolcap)
	} else {
		slot = 0
		poolslot = index
	}

	if slot < 0 || slot >= len(pl.pools) {
		log.LogError("(GetObject)not found slot position,", slot)
		return nil
	}

	p := pl.pools[slot]

	if poolslot < 0 || poolslot >= p.capacity {
		log.LogError("(GetObject)not found pool slot position,", poolslot)
		return nil
	}

	return p.GetSlot(index)
}

func (pl *PoolList) ClearAll() {
	for _, p := range pl.pools {
		for i, _ := range p.pool {
			p.pool[i] = nil
		}
		p.count = 0
	}
}
